﻿using SelfManagement.Account.Common;
using SelfManagement.Common.Exceptions;
using SelfManagement.Database;
using SelfManagement.Database.POCO;
using Ninject;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Net.Mail;
using System.Text;
using System.Threading.Tasks;
using SelfManagement.Utility.Extensions;
using SelfManagement.Utility.Helper;

namespace SelfManagement.Account.Manager
{
    public class UserManager : BaseManager
    {
        #region ValidateUser
        
        public virtual bool ValidateUser(string userName, string password)
        {
            UserStatus userStatus;
            return ValidateUser(userName, password, out userStatus);
        }

        public virtual bool ValidateUser(string userName, string password, out UserStatus userStatus)
        {
            var user = UserProvider.Get(a => a.UserName == userName || a.Email == userName || a.Phone == userName);

            Setting setting = this.SettingProvider.Get();

            bool valid = false;
            userStatus = UserStatus.Unactive;

            if (user != null)
            {
                //用户还未激活
                if (user.Status == UserStatus.Unactive)
                {
                    userStatus = UserStatus.Unactive;
                    //return true;
                }

                var encodedPassword = PasswordProvider.EncryptPassword(password);
                valid = encodedPassword == user.Password;

                //超时解锁
                if (user.Status == UserStatus.Locked && user.LastLockoutDate.HasValue
                    && user.LastLockoutDate.Value.AddMinutes(setting.MinutesToUnlock) < DateTime.Now)
                {
                    user.FailedPasswordAttemptCount = 0;
                    user.Status = UserStatus.Normal;
                }

                if (valid)
                {
                    user.FailedPasswordAttemptCount = 0;
                    user.LastLoginDate = DateTime.UtcNow;
                    if (user.Status == UserStatus.Locked || user.Status == UserStatus.Normal)//解锁
                    {
                        user.Status = userStatus = UserStatus.Normal;
                    }
                }
                else
                {
                    user.FailedPasswordAttemptCount++;
                    if (user.FailedPasswordAttemptCount >= setting.FailedPasswordAttemptCount)
                    {
                        user.Status = userStatus = UserStatus.Locked;
                        user.LastLockoutDate = DateTime.Now;
                    }
                }

                Update(user);
            }

            return valid;
        }

        #endregion

        #region Get

        public virtual User Get(string userName)
        {
            return UserProvider.Get(a => a.UserName == userName || a.Email == userName || a.Phone == userName);
        }

        public virtual User Get(Expression<Func<User, bool>> predicate)
        {
            return UserProvider.Get(predicate);
        }

        #endregion

        #region Update

        public virtual void Update(User user)
        {
            #region Validate data

            ValidateData(user, false);

            #endregion

            UserProvider.Update(user);
        }

        #endregion

        #region Add

        public virtual bool Add(User user, bool saveChange = true, bool genPassword = false)
        {
            #region Validate data

            
            if (genPassword && user.Password.IsNullOrWhiteSpace())
            {
                user.Password = UniqueIdGenerator.GetInstance().GetBase32UniqueId(6).ToLower();//如果没有设置密码，生成默认密码
            }

            ValidateData(user);

            #endregion
            
            user.Password = PasswordProvider.EncryptPassword(user.Password);
            
            return UserProvider.Insert(user, saveChange);
        }

        #endregion

        #region Delete

        public virtual bool Delete(string userName)
        {
            User user = Get(userName);
            if (user.IsNotNull())
            {
                user.IsDelete = true;
                UserProvider.Update(user);
            }
            return true;
        }

        public virtual bool Delete(Expression<Func<User, bool>> predicate)
        {
            return UserProvider.UpdateBatch(predicate, u =>
            {
                u.IsDelete = true;
                return u;
            });
        }

        #endregion

        #region All

        public virtual IEnumerable<User> All()
        {
            return UserProvider.Query().AsEnumerable();
        }

        #endregion

        #region ChangePassword

        public virtual bool ChangePassword(string userName, string oldPassword, string newPassword)
        {
            if (!ValidateUser(userName, oldPassword))
            {
                return false;
            }
            return ChangePassword(userName, newPassword);
        }

        public virtual bool ChangePassword(string userName, string newPassword)
        {
            var user = UserProvider.Get(u => u.UserName == userName);

            if (user == null)
            {
                throw new ArgumentException("The member doest not exists.");
            }

            user.Password = PasswordProvider.EncryptPassword(newPassword); ;

            Update(user);

            return true;
        }

        public virtual bool SetIfChangePassword(User user, string oldPassword, string newPassword)
        {
            if (newPassword.IsNotNullAndNotWhiteSpace())
            {
                if (PasswordProvider.EncryptPassword(oldPassword) != user.Password)
                {
                    return false;
                }
                user.Password = PasswordProvider.EncryptPassword(newPassword);
            }
            return true;
        }

        #endregion

        private void ValidateData(User user, bool creating = true)
        {
            List<DataViolationItem> violations = new List<DataViolationItem>();

            if (creating && UserProvider.Get(a => a.UserName == user.UserName) != null)
            {
                violations.Add(new DataViolationItem("UserName", user.UserName, "用户名被占用"));
            }
            if (string.IsNullOrEmpty(user.Password))
            {
                violations.Add(new DataViolationItem("Password", user.Password, "密码格式无效"));
            }

            if (violations.Count > 0)
            {
                throw new DataViolationException(violations);
            }
        }
    }
}
